home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
sig
/
signals.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
42KB
|
1,408 lines
/*
* signals.c --
*
* Copyright 1988 Regents of the University of California.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*
* This contains routines that deal with Sprite signals. See the man pages
* on signals for an explanation of the Sprite signaling facilities. The
* only thing that is explained in these comments is the implementation of
* these facilities.
*
* SYNCHRONIZATION
*
* Whenever the signal state of a process is modified, the sig monitor lock
* is usually grabbed, and the process is usually locked (e.g., LocalSend).
* Code that doesn't obtain the sig monitor lock is in migration and
* signals initialization (e.g., fork & exec); the process is locked in
* these cases. Currently (18-Dec-1991) code that doesn't lock the process
* (e.g., some calls to SigClearPendingMask) only works on the current
* process (i.e., a process is changing its own pending signals list), and
* is therefore unlikely to interfere with the code that didn't obtain the
* sig monitor lock. (XXX This really ought to get cleaned up.)
*
* When the signal state is looked at no locking is done. It is assumed
* that there are two ways that the signal state will be looked at:
*
* 1) A process in the middle of executing a system call
* will check to see if any signals are pending before waiting for
* an extended period (i.e. waiting for a read to complete). If
* not then it will go to sleep until either a signal comes in or
* the thing that it is waiting for completes. This does not
* require any synchronization on reading because the routine
* which is used to put a process to sleep (see Sync_WaitEventInt)
* will check for signals with the master lock down before the
* process is put to sleep. If there are signals pending, then
* the sleep call will return immediately. Otherwise if a signal
* comes in after the process goes to sleep then it will be
* awakened by the Sig_Send which calls Sync_WakeWaitingProcess which
* synchronizes correctly with the Sync_Wait calls. Thus there
* is no way to miss a signal in this case.
*
* 2) A process is returning to user mode after trapping into the kernel
* for some reason and it wants to see if signals are pending before
* it returns. In this case the trap handler (see Exc_Trap) will
* disable interrupts before checking to see if signals are pending.
* If they are then it will enable interrupts and process the signal.
* Otherwise it will return to user mode with interrupts being enabled
* on the return to user mode. If a signal came in when
* interrupts were disabled then once interrupts are enabled the
* process will be interrupted and return back into the kernel.
* Likewise once the user process returns to user mode if a signal is
* delivered then the user process will be interrupted. Interruption
* is possible of course only on a multi-processor. Once interrupted it
* will be forced back into the kernel where it will discover a
* signal. Thus a signal cannot be missed in this case either.
*
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/sig/signals.c,v 9.15 92/04/10 16:42:00 kupfer Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <stdlib.h>
#include <sig.h>
#include <sync.h>
#include <dbg.h>
#include <list.h>
#include <proc.h>
#include <procMigrate.h>
#include <status.h>
#include <sched.h>
#include <sigInt.h>
#include <rpc.h>
#include <net.h>
#include <vm.h>
#include <bstring.h>
#include <stdio.h>
unsigned int sigBitMasks[SIG_NUM_SIGNALS];
int sigDefActions[SIG_NUM_SIGNALS];
int sigCanHoldMask;
Sync_Lock sigLock;
Sync_Condition signalCondition;
static void LocalSend _ARGS_((Proc_ControlBlock *procPtr, int sigNum, int code,
Address addr));
/*
*----------------------------------------------------------------------
*
* Sig_Init --
*
* Initialize the signal data structures.
*
* Results:
* None.
*
* Side effects:
* The set of bit masks and the set of default actions are set up.
*
*----------------------------------------------------------------------
*/
void
Sig_Init()
{
int i;
Sync_LockInitDynamic(&sigLock, "Sig:sigLock");
for (i = SIG_MIN_SIGNAL; i < SIG_NUM_SIGNALS; i++) {
sigBitMasks[i] = Sig_NumberToMask(i);
sigDefActions[i] = SIG_KILL_ACTION;
}
/*
* Note that SIG_RESUME uses the "kill" action, even though it's not
* actually used to kill the process.
*/
sigDefActions[SIG_DEBUG] = SIG_DEBUG_ACTION;
sigDefActions[SIG_ARITH_FAULT] = SIG_DEBUG_ACTION;
sigDefActions[SIG_ILL_INST] = SIG_DEBUG_ACTION;
sigDefActions[SIG_ADDR_FAULT] = SIG_DEBUG_ACTION;
sigDefActions[SIG_BREAKPOINT] = SIG_DEBUG_ACTION;
sigDefActions[SIG_TRACE_TRAP] = SIG_DEBUG_ACTION;
sigDefActions[SIG_MIGRATE_TRAP] = SIG_MIGRATE_ACTION;
sigDefActions[SIG_MIGRATE_HOME] = SIG_MIGRATE_ACTION;
sigDefActions[SIG_SUSPEND] = SIG_SUSPEND_ACTION;
sigDefActions[SIG_TTY_INPUT] = SIG_SUSPEND_ACTION;
sigDefActions[SIG_URGENT] = SIG_IGNORE_ACTION;
sigDefActions[SIG_CHILD] = SIG_IGNORE_ACTION;
sigDefActions[SIG_TTY_SUSPEND] = SIG_SUSPEND_ACTION;
sigDefActions[SIG_TTY_OUTPUT] = SIG_SUSPEND_ACTION;
sigDefActions[SIG_IO_READY] = SIG_IGNORE_ACTION;
sigDefActions[SIG_WINDOW_CHANGE] = SIG_IGNORE_ACTION;
sigCanHoldMask =
~(sigBitMasks[SIG_ARITH_FAULT] | sigBitMasks[SIG_ILL_INST] |
sigBitMasks[SIG_ADDR_FAULT] | sigBitMasks[SIG_KILL] |
sigBitMasks[SIG_BREAKPOINT] | sigBitMasks[SIG_TRACE_TRAP] |
sigBitMasks[SIG_MIGRATE_HOME] | sigBitMasks[SIG_SUSPEND]);
}
/*
*----------------------------------------------------------------------
*
* Sig_ProcInit --
*
* Initialize the signal data structures for the first process.
*
* Results:
* None.
*
* Side effects:
* Signal state initialized.
*
*----------------------------------------------------------------------
*/
void
Sig_ProcInit(procPtr)
register Proc_ControlBlock *procPtr;
{
procPtr->sigHoldMask = 0;
procPtr->sigPendingMask = 0;
bcopy((Address)sigDefActions,(Address)procPtr->sigActions,
sizeof(sigDefActions));
bzero((Address)procPtr->sigMasks,sizeof(procPtr->sigMasks));
bzero((Address)procPtr->sigCodes,sizeof(procPtr->sigCodes));
procPtr->sigFlags = 0;
}
/*
*----------------------------------------------------------------------
*
* Sig_Fork --
*
* Copy over the parents signal state into the child.
*
* Results:
* None.
*
* Side effects:
* Signal state copied from parent to child and pending mask cleared in
* child. Migration is held until the first return into user mode.
*
*----------------------------------------------------------------------
*/
void
Sig_Fork(parProcPtr, childProcPtr)
register Proc_ControlBlock *parProcPtr;
register Proc_ControlBlock *childProcPtr;
{
/*
* Copy the parent's signal state to the child. Set up migration
* to be held initially. On the first return to user mode, after
* signals are processed, migration will be reenabled.
*/
childProcPtr->sigHoldMask = parProcPtr->sigHoldMask |
Sig_NumberToMask(SIG_MIGRATE_TRAP);
childProcPtr->sigPendingMask = 0;
bcopy((Address)parProcPtr->sigActions,
(Address)childProcPtr->sigActions,
sizeof(childProcPtr->sigActions));
bcopy((Address)parProcPtr->sigMasks,
(Address)childProcPtr->sigMasks,
sizeof(childProcPtr->sigMasks));
bzero((Address)childProcPtr->sigCodes,sizeof(childProcPtr->sigCodes));
childProcPtr->sigFlags = 0;
}
/*
*----------------------------------------------------------------------
*
* Sig_Exec --
*
* Clear all signal handlers on exec. Assumed called with the proc
* table entry locked such that signals against this process are
* prevented.
*
* Results:
* None.
*
* Side effects:
* All signal handlers are cleared and the pending mask is cleared.
*
*----------------------------------------------------------------------
*/
void
Sig_Exec(procPtr)
Proc_ControlBlock *procPtr;
{
register int *actionPtr;
register int i;
for (i = SIG_MIN_SIGNAL, actionPtr = &procPtr->sigActions[SIG_MIN_SIGNAL];
i < SIG_NUM_SIGNALS;
i++, actionPtr++) {
if (*actionPtr > SIG_SUSPEND_ACTION) {
/*
* The action contains a signal handler to call. Reset back to
* the default action.
*/
*actionPtr = sigDefActions[i];
procPtr->sigMasks[i] = 0;
}
}
procPtr->sigPendingMask = 0;
}
/*
*----------------------------------------------------------------------
*
* Sig_ChangeState --
*
* Set the entire signal state of the process to that given. When
* setting the state verify that improper signals are not blocked or
* ignored. The process is assumed to be locked.
*
* Results:
* None.
*
* Side effects:
* The signal actions and hold mask will be set for the process.
* Might change the suspend/resume flags in the PCB.
*
*----------------------------------------------------------------------
*/
ENTRY void
Sig_ChangeState(procPtr, actions, sigMasks, pendingMask, sigCodes, holdMask)
register Proc_ControlBlock *procPtr;
int actions[];
register int sigMasks[];
int pendingMask;
int sigCodes[];
int holdMask;
{
register int i;
register int *actionPtr;
LOCK_MONITOR;
for (i = SIG_MIN_SIGNAL, actionPtr = &actions[SIG_MIN_SIGNAL];
i < SIG_NUM_SIGNALS;
i++, actionPtr++) {
if (i == SIG_KILL) {
continue;
}
procPtr->sigActions[i] = *actionPtr;
if (*actionPtr == SIG_IGNORE_ACTION) {
/*
* If is ignore action then make sure that is not one of the
* signals that cannot be ignored. If not then remove the signal
* from the pending mask.
*/
if (sigBitMasks[i] & sigCanHoldMask) {
pendingMask &= ~sigBitMasks[i];
} else {
procPtr->sigActions[i] = sigDefActions[i];
}
} else if (*actionPtr > SIG_NUM_ACTIONS) {
/*
* If greater than one of the actions then must be the address
* of a signal handler so store the signal mask.
*/
procPtr->sigMasks[i] = sigMasks[i] & sigCanHoldMask;
}
}
procPtr->sigPendingMask = pendingMask;
/*
* Make sure the suspend/resume flags are consistent with the pending
* signals mask.
*/
procPtr->genFlags &= ~PROC_RESUME_PROCESS;
if (pendingMask & Sig_NumberToMask(SIG_SUSPEND)) {
procPtr->genFlags |= PROC_PENDING_SUSPEND;
} else {
procPtr->genFlags &= ~PROC_PENDING_SUSPEND;
}
procPtr->sigHoldMask = holdMask & sigCanHoldMask;
bcopy((Address) sigCodes, (Address) procPtr->sigCodes,
sizeof(procPtr->sigCodes));
procPtr->specialHandling = 1;
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Sig_UserSend --
* Send a signal to a process. Call the internal routine to do the
* work.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Sig_UserSend(sigNum, pid, familyID)
int sigNum; /* The signal to send. */
Proc_PID pid; /* The id number of the process or process
family. */
Boolean familyID; /* Whether the id is a process id or a process
group id. */
{
return(Sig_Send(sigNum, SIG_NO_CODE, pid, familyID, (Address)0));
}
/*
*----------------------------------------------------------------------
*
* LocalSend --
*
* Send a signal to a process on the local machine. It assumed that the
* process is locked down when we are called.
*
* Results:
* None.
*
* Side effects:
* Signal pending mask and code modified. The process's suspend and
* resume flags might also get changed.
*
*----------------------------------------------------------------------
*/
ENTRY static void
LocalSend(procPtr, sigNum, code, addr)
register Proc_ControlBlock *procPtr;
int sigNum;
int code;
Address addr;
{
int sigBitMask;
LOCK_MONITOR;
/*
* Signals can't be sent to kernel processes unless the system is being
* shutdown since kernel processes never get the opportunity to handle
* signals.
*/
if ((procPtr->genFlags & PROC_KERNEL) && !sys_ShuttingDown) {
UNLOCK_MONITOR;
return;
}
if ((procPtr->sigActions[sigNum] == SIG_DEBUG_ACTION) &&
proc_KillMigratedDebugs && (procPtr->genFlags & PROC_FOREIGN)) {
/*
* Kill the process rather than letting it go silently into that
* good night (on the wrong machine). Debugging migrated
* processes is nasty. It would be nice if we could redirect
* the printf to the process's home node, too.
*/
sigNum = SIG_KILL;
if (proc_MigDebugLevel > 1) {
printf("Warning: killing a migrated process that would have gone into the debugger, pid %x rpid %x uid %d.\n",
procPtr->processID, (int) procPtr->peerProcessID,
procPtr->userID);
}
}
/*
* Only send the signal if it shouldn't be ignored and if it isn't
* a signal to migrate an unmigrated process. (The latter can easily
* happen when signalling a process family to migrate home.)
*/
if ((procPtr->sigActions[sigNum] != SIG_IGNORE_ACTION) &&
!((sigNum == SIG_MIGRATE_HOME) && (procPtr->peerHostID == NIL))) {
if (sigNum == SIG_RESUME) {
/*
* Resume the suspended process.
*/
Proc_ResumeProcess(procPtr, FALSE);
}
if (procPtr->sigActions[sigNum] == SIG_SUSPEND_ACTION &&
procPtr->state == PROC_SUSPENDED) {
/*
* Are sending a suspend signal to a process that is already
* suspended. In this case just notify the parent that the
* process has been suspended. This is necessary because resume
* signals are sent by processes to debugged processes which do not
* really get resumed. However, the signaling process will not
* be informed that the process it sent the signal to did not get
* resumed (SIG_RESUME works regardless whether it actually
* resumes anything or not). Thus a process may believe that
* a process is running even though it really isn't and it may
* send a suspend signal to an already suspended process.
*
* There is a potential race here between a process getting
* suspended and us checking here but it doesn't matter. If
* it gets suspended after we check then the parent will get
* notified anyway.
*/
Proc_InformParent(procPtr, PROC_SUSPEND_STATUS);
} else if (sigNum != SIG_RESUME ||
procPtr->sigActions[sigNum] != SIG_KILL_ACTION) {
sigBitMask = sigBitMasks[sigNum];
procPtr->sigPendingMask |= sigBitMask;
procPtr->sigCodes[sigNum] = code;
procPtr->sigAddr = (int)addr;
if (sigNum == SIG_SUSPEND) {
/*
* Set the "pending suspend" flag in case the process is
* resumed before it actually suspends. Clear the "resume"
* flag in case the process is resumed and then suspended
* again before the first suspend is processed.
*/
procPtr->genFlags |= PROC_PENDING_SUSPEND;
procPtr->genFlags &= ~PROC_RESUME_PROCESS;
}
if (procPtr->sigHoldMask & sigBitMask & ~sigCanHoldMask) {
/*
* We received a signal that was blocked but can't be blocked
* by users. It only can be blocked if we are in the middle of
* executing a signal handler for the signal. So we set things
* up to take the default action and make the signal unblocked
* so that we don't get an infinite loop of errors.
*/
procPtr->sigHoldMask &= ~sigBitMask;
procPtr->sigActions[sigNum] = sigDefActions[sigNum];
}
procPtr->specialHandling = 1;
/*
* If the process is waiting then wake it up.
*/
Sync_WakeWaitingProcess(procPtr);
if (sigNum == SIG_KILL || sigNum == SIG_MIGRATE_TRAP ||
sigNum == SIG_MIGRATE_HOME) {
if (sigNum == SIG_KILL && procPtr->state == PROC_NEW &&
(procPtr->genFlags & PROC_FOREIGN)) {
/*
* The process was only partially created. We can't make
* it runnable so we have to reclaim it directly.
* Do this in the background so that
* Proc_DestroyMigratedProc has to wait for Sig_Send
* to unlock the process and we avoid a race condition.
*/
Proc_CallFunc(Proc_DestroyMigratedProc,
(ClientData) procPtr->processID, 0);
} else {
/*
* Resume the process so that we can perform the signal.
* If we're killing it, we tell Proc_ResumeProcess so it
* will even wake up a debugged process.
*/
Proc_ResumeProcess(procPtr,
(sigNum == SIG_KILL) ? TRUE : FALSE);
}
}
}
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Sig_SendProc --
*
* Store the signal in the pending mask and store the code for the given
* process. The code and addr are passed to the user interrupt
* handler. The code indicates the cause of the signal. The addr
* indicates the address of the fault.
*
* NOTE: Assumes that we are called without the master lock down and
* with the process locked.
*
* Results:
* In the case of a local process, SUCCESS is returned. If the process
* is migrated, error conditions such as RPC_TIMEOUT may be returned.
*
* Side effects:
* Signal pending mask and code modified. If the process being signalled
* is migrated, an RPC is sent. If the process is local, the sched_Mutex
* master lock is grabbed.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Sig_SendProc(procPtr, sigNum, code, addr)
register Proc_ControlBlock *procPtr;
int sigNum;
int code;
Address addr;
{
ReturnStatus status;
/*
* Make sure that the signal is in range.
*/
if (sigNum < SIG_MIN_SIGNAL || sigNum >= SIG_NUM_SIGNALS) {
if (sigNum == 0) {
return(SUCCESS);
} else {
return(SIG_INVALID_SIGNAL);
}
}
/*
* Handle migrated processes specially. There's a race condition
* when sending a signal to a migrated process, since it can
* migrate back to this host while we're doing it. Therefore,
* if the problem was that the process didn't exist, check
* to see if it has migrated back to this host (it's no longer MIGRATED).
* We don't have to check for MIGRATING, since SigMigSend waits for
* a migration in progress to complete. Also make sure that while the
* signal is sent and the process is unlocked, it processID doesn't change.
*/
if (procPtr->state == PROC_MIGRATED ||
(procPtr->genFlags & PROC_MIGRATING)) {
Proc_PID processID;
processID = procPtr->processID;
status = SigMigSend(procPtr, sigNum, code, addr);
if (processID != procPtr->processID) {
return(status);
}
if ((status != PROC_INVALID_PID) ||
(procPtr->state == PROC_MIGRATED)) {
return(status);
}
}
if (procPtr->state == PROC_EXITING) {
return(PROC_INVALID_PID);
} else if (procPtr->state == PROC_NEW) {
if (procPtr->genFlags & PROC_FOREIGN && proc_MigDebugLevel > 0) {
printf("Warning: got signal for process %x before migration complete.\n",
procPtr->processID);
}
return(PROC_INVALID_PID);
} else {
LocalSend(procPtr, sigNum, code, addr);
return(SUCCESS);
}
}
/*
*----------------------------------------------------------------------
*
* Sig_Send --
*
* Send a signal to a process. This entails marking the signal into
* the signal pending mask for the process and waking up the process
* if it is asleep.
*
* Results:
* An error is the signal or the process id are invalid. SUCCESS
* otherwise.
*
* Side effects:
* The signal information in the proc table for the process that
* is being sent the signal may be modified.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Sig_Send(sigNum, code, id, familyID, addr)
int sigNum; /* The signal to send. */
int code; /* The code that goes with the signal. */
Proc_PID id; /* The id number of the process or process
family. */
Boolean familyID; /* Whether the id is a process id or a process
group id. */
Address addr; /* The address of the fault */
{
register Proc_ControlBlock *procPtr;
Proc_PCBLink *procLinkPtr;
ReturnStatus status;
List_Links *familyList;
int userID;
int hostID;
if (!Proc_ComparePIDs(id, PROC_MY_PID)) {
hostID = Proc_GetHostID(id);
if (hostID != rpc_SpriteID) {
/*
* Send a remote signal.
*/
if (hostID == NET_BROADCAST_HOSTID ||
hostID > NET_NUM_SPRITE_HOSTS || hostID < 0) {
return(PROC_INVALID_PID);
} else {
return(SigSendRemoteSignal(hostID, sigNum, code, id,
familyID, addr));
}
}
}
/*
* Get the pointer to the control block if this is a valid process id.
*/
if (!familyID) {
if (Proc_ComparePIDs(id, PROC_MY_PID)) {
procPtr = Proc_GetEffectiveProc();
if (procPtr == (Proc_ControlBlock *) NIL) {
panic("Sig_Send: procPtr == NIL\n");
}
Proc_Lock(procPtr);
} else {
procPtr = Proc_LockPID(id);
if (procPtr == (Proc_ControlBlock *) NIL) {
return(PROC_INVALID_PID);
}
if (!Proc_HasPermission(procPtr->effectiveUserID)) {
Proc_Unlock(procPtr);
return(PROC_UID_MISMATCH);
}
}
status = Sig_SendProc(procPtr, sigNum, code, addr);
Proc_Unlock(procPtr);
} else {
Proc_PID *pidArray;
int i;
int numProcs;
status = Proc_LockFamily((int)id, &familyList, &userID);
if (status != SUCCESS) {
return(status);
}
if (!Proc_HasPermission(userID)) {
Proc_UnlockFamily((int)id);
return(PROC_UID_MISMATCH);
}
/*
* Send a signal to everyone in the given family. We do this
* by grabbing a list of process IDs and then sending the signals
* with the family not locked, to avoid deadlocks resulting from
* signals being sent with the family locked.
*/
numProcs = 0;
LIST_FORALL(familyList, (List_Links *) procLinkPtr) {
numProcs++;
}
pidArray = (Proc_PID *) malloc(numProcs * sizeof(Proc_PID));
i = 0;
LIST_FORALL(familyList, (List_Links *) procLinkPtr) {
procPtr = procLinkPtr->procPtr;
Proc_Lock(procPtr);
pidArray[i] = procPtr->processID;
Proc_Unlock(procPtr);
i++;
if (i > numProcs) {
panic("Sig_Send: process family changed size while locked.\n");
free((Address) pidArray);
return(FAILURE);
}
}
Proc_UnlockFamily((int)id);
for (i = 0; i < numProcs; i++) {
procPtr = Proc_LockPID(pidArray[i]);
if (procPtr == (Proc_ControlBlock *) NIL) {
/*
* Race condition: process got removed.
*/
continue;
}
status = Sig_SendProc(procPtr, sigNum, code, addr);
Proc_Unlock(procPtr);
if (status != SUCCESS) {
break;
}
}
free((Address) pidArray);
}
return(status);
}
typedef struct {
int sigNum;
int code;
Proc_PID id;
Boolean familyID;
int effUid;
Address addr;
} SigParms;
/*
*----------------------------------------------------------------------
*
* SigSendRemoteSignal --
*
* Send a signal to a process on a remote machine.
*
* Results:
* Return the status from the remote machine.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
SigSendRemoteSignal(hostID, sigNum, code, id, familyID, addr)
int hostID; /* Host to send message to. */
int sigNum; /* Signal to send. */
int code; /* Code to send. */
Proc_PID id; /* ID to send it to. */
Boolean familyID; /* TRUE if are sending to a process family. */
Address addr; /* Address of signal. */
{
SigParms sigParms;
Rpc_Storage storage;
Proc_ControlBlock *procPtr;
sigParms.sigNum = sigNum;
sigParms.code = code;
sigParms.id = id;
sigParms.familyID = familyID;
procPtr = Proc_GetEffectiveProc();
sigParms.effUid = procPtr->effectiveUserID;
sigParms.addr = addr;
storage.requestParamPtr = (Address)&sigParms;
storage.requestParamSize = sizeof(sigParms);
storage.requestDataPtr = (Address)NIL;
storage.requestDataSize = 0;
storage.replyParamPtr = (Address)NIL;
storage.replyParamSize = 0;
storage.replyDataPtr = (Address)NIL;
storage.replyDataSize = 0;
return(Rpc_Call(hostID, RPC_SIG_SEND, &storage));
}
/*
*----------------------------------------------------------------------
*
* Sig_RpcSend --
*
* Stub to handle a remote signal RPC.
*
* Results:
* SUCCESS.
*
* Side effects:
* Reply is sent.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
Sig_RpcSend(srvToken, clientID, command, storagePtr)
ClientData srvToken; /* Handle on server process passed to
* Rpc_Reply */
int clientID; /* Sprite ID of client host */
int command; /* Command identifier */
register Rpc_Storage *storagePtr; /* The request fields refer to the
* request buffers and also indicate
* the exact amount of data in the
* request buffers. The reply fields
* are initialized to NIL for the
* pointers and 0 for the lengths.
* This can be passed to Rpc_Reply */
{
SigParms *sigParmsPtr;
ReturnStatus status;
Proc_ControlBlock *procPtr;
int effUid;
sigParmsPtr = (SigParms *) storagePtr->requestParamPtr;
procPtr = Proc_GetCurrentProc();
effUid = procPtr->effectiveUserID;
procPtr->effectiveUserID = sigParmsPtr->effUid;
status = Sig_Send(sigParmsPtr->sigNum, sigParmsPtr->code, sigParmsPtr->id,
sigParmsPtr->familyID, sigParmsPtr->addr);
procPtr->effectiveUserID = effUid;
Rpc_Reply(srvToken, status, storagePtr, (int(*)())NIL, (ClientData)NIL);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Sig_SetHoldMask --
*
* Set the signal hold mask for the current process. Return the
* old mask. No synchronization required since the only process
* that can modify the hold mask is this process.
*
* Results:
* Error if the place to store the old mask is invalid, SUCCESS
* otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Sig_SetHoldMask(newMask, oldMaskPtr)
int newMask; /* Mask to set the hold mask to. */
int *oldMaskPtr; /* Where to store the old mask. */
{
register Proc_ControlBlock *procPtr;
/*
* Get out the old mask value and store the new one.
*/
procPtr = Proc_GetActualProc();
if (oldMaskPtr != USER_NIL) {
if (Vm_CopyOut(sizeof(procPtr->sigHoldMask),
(Address) &(procPtr->sigHoldMask),
(Address) oldMaskPtr) != SUCCESS) {
return(SYS_ARG_NOACCESS);
}
}
procPtr->sigHoldMask = newMask & sigCanHoldMask;
procPtr->specialHandling = 1;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Sig_SetAction --
*
* Set the action for a particular signal.
*
* Results:
* Error if the action, signal, or handler is invalid.
*
* Side effects:
* The sigAction and sigMasks fields may be modified for the
* particular signal.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Sig_SetAction(sigNum, newActionPtr, oldActionPtr)
int sigNum; /* The signal for which the action is to be
set. */
Sig_Action *newActionPtr; /* The actions to take for the signal. */
Sig_Action *oldActionPtr; /* The action that was taken for the signal. */
{
Proc_ControlBlock *procPtr;
Address dummy;
Sig_Action action;
/*
* Make sure that the signal is in range.
*/
if (sigNum < SIG_MIN_SIGNAL || sigNum >= SIG_NUM_SIGNALS ||
sigNum == SIG_KILL || sigNum == SIG_SUSPEND) {
return(SIG_INVALID_SIGNAL);
}
procPtr = Proc_GetActualProc();
/*
* Copy out the current action. There are two cases:
*
* 1) The current action really contains a handler to call. Thus
* the current action is SIG_HANDLE_ACTION.
* 2) The current action is one of the other four actions.
*/
if (oldActionPtr != (Sig_Action *) USER_NIL) {
if (procPtr->sigActions[sigNum] > SIG_NUM_ACTIONS) {
action.action = SIG_HANDLE_ACTION;
action.handler = (int (*)())procPtr->sigActions[sigNum];
action.sigHoldMask = procPtr->sigMasks[sigNum];
} else {
if (procPtr->sigActions[sigNum] == sigDefActions[sigNum]) {
action.action = SIG_DEFAULT_ACTION;
} else {
action.action = procPtr->sigActions[sigNum];
}
}
if (Vm_CopyOut(sizeof(action), (Address) &action,
(Address) oldActionPtr) != SUCCESS) {
return(SYS_ARG_NOACCESS);
}
}
/*
* Copy in the action to take.
*/
if (Vm_CopyIn(sizeof(action), (Address) newActionPtr,
(Address) &action) != SUCCESS) {
return(SYS_ARG_NOACCESS);
}
/*
* Make sure that the action is valid.
*/
if (action.action < 0 || action.action > SIG_NUM_ACTIONS) {
return(SIG_INVALID_ACTION);
}
if (action.action == SIG_DEFAULT_ACTION) {
action.action = sigDefActions[sigNum];
}
/*
* Store the action. If it is SIG_HANDLE_ACTION then the handler is stored
* in place of the action.
*/
if (action.action == SIG_HANDLE_ACTION) {
if (Vm_CopyIn(4, (Address) ((unsigned int) (action.handler)),
(Address) &dummy) != SUCCESS) {
return(SYS_ARG_NOACCESS);
}
procPtr->sigMasks[sigNum] =
(sigBitMasks[sigNum] | action.sigHoldMask) & sigCanHoldMask;
procPtr->sigActions[sigNum] = (unsigned int) action.handler;
} else if (action.action == SIG_IGNORE_ACTION) {
/*
* Only actions that can be blocked can be ignored. This prevents a
* user from ignoring a signal such as a bus error which would cause
* the process to take a bus error repeatedly.
*/
if (sigBitMasks[sigNum] & sigCanHoldMask) {
procPtr->sigActions[sigNum] = SIG_IGNORE_ACTION;
Proc_Lock(procPtr);
SigClearPendingMask(procPtr, sigNum);
if (sigNum == SIG_SUSPEND) {
procPtr->genFlags &= ~(PROC_PENDING_SUSPEND |
PROC_RESUME_PROCESS);
}
Proc_Unlock(procPtr);
} else {
return(SIG_INVALID_SIGNAL);
}
procPtr->sigMasks[sigNum] = 0;
} else {
procPtr->sigActions[sigNum] = action.action;
procPtr->sigMasks[sigNum] = 0;
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Sig_Pause --
*
* Atomically change signal hold mask and wait for a signal to arrive.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
Sig_Pause(sigHoldMask)
int sigHoldMask; /* The value that the mask of held signals is to be set
to while waiting for a signal to arrive. */
{
register Proc_ControlBlock *procPtr;
ReturnStatus status;
int migMask;
LOCK_MONITOR;
procPtr = Proc_GetActualProc();
/*
* The signal mask cannot be restored until the signal handler has
* had a chance to be called for the signal that caused Sig_Pause
* to return. To allow this the current hold mask is stored in the
* proc table and the flag sigPause is set to be true to indicate that
* the hold mask has to be restored after the signal handler has had a
* chance to be called.
*/
procPtr->oldSigHoldMask = procPtr->sigHoldMask;
procPtr->sigFlags |= SIG_PAUSE_IN_PROGRESS;
procPtr->sigHoldMask = sigHoldMask & sigCanHoldMask;
procPtr->specialHandling = 1;
/*
* Wait on the signal condition. As it turns out since a signal
* wakes up the process regardless what it is sleeping on, this condition
* variable is never broadcasted on, but we have to wait on something in
* order to release the monitor lock.
*
* Don't let a Sig_Pause be interrupted by a migrate trap signal.
* So, if none of the signal bits are set besides migration-related
* signals, and a migration-related signal bit is set, let the user-level
* code retry the signal.
*/
(void) Sync_Wait(&signalCondition, TRUE);
migMask = (Sig_NumberToMask(SIG_MIGRATE_TRAP)) |
(Sig_NumberToMask(SIG_MIGRATE_HOME));
if ((! (procPtr->sigPendingMask & ~migMask)) &&
(procPtr->sigPendingMask & migMask)) {
status = GEN_ABORTED_BY_SIGNAL;
} else {
status = SUCCESS;
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* SigClearPendingMask --
*
* Remove the given signal from the pending mask.
*
* Results:
* None.
*
* Side effects:
* Pending mask for process modified.
*
*----------------------------------------------------------------------
*/
ENTRY void
SigClearPendingMask(procPtr, sigNum)
register Proc_ControlBlock *procPtr;
int sigNum;
{
LOCK_MONITOR;
procPtr->sigPendingMask &= ~sigBitMasks[sigNum];
UNLOCK_MONITOR;
}
/*
*---------------------------------------------------------------------------
*
* Routines for signal handlers --
*
* A signal handler is called right before a process is to return to
* user space. In order to do this the current state before the signal
* is taken must be saved, the signal handler called, and then the state
* restored when the signal handler returns. It is this modules responsibility
* to handle the signal state; all of the actual saving and restoring of
* machine state and the calling of the handler is done in the machine
* dependent routines in the mach module.
*/
/*
*----------------------------------------------------------------------
*
* Sig_Handle --
*
* Set things up so that the signal handler is called for one of the
* signals that are pending for the current process. This is done
* by saving the old trap stack and modifying the current one.
*
* Results:
* Return TRUE if a signal is setup to be handled by the user.
*
* Side effects:
* *trapStackPtr is modified and also the user stack is modified.
*
*----------------------------------------------------------------------
*/
Boolean
Sig_Handle(procPtr, sigStackPtr, pcPtr)
register Proc_ControlBlock *procPtr;
register Sig_Stack *sigStackPtr;
Address *pcPtr;
{
int sigs;
int sigNum;
unsigned int *bitMaskPtr;
int sigBitMask;
/*
* Find out which signals are pending.
*/
sigs = procPtr->sigPendingMask & ~procPtr->sigHoldMask;
if (sigs == 0) {
return(FALSE);
}
/*
* Check for the signal SIG_KILL. This is processed specially because
* it is how processes that have some problem such as being unable
* to write to swap space on the file server are destroyed.
*/
if (sigs & sigBitMasks[SIG_KILL]) {
if (procPtr->sigCodes[SIG_KILL] != SIG_NO_CODE) {
Proc_ExitInt(PROC_TERM_DESTROYED,
procPtr->sigCodes[SIG_KILL], 0);
} else {
Proc_ExitInt(PROC_TERM_SIGNALED, SIG_KILL, 0);
}
}
for (sigNum = SIG_MIN_SIGNAL, bitMaskPtr = &sigBitMasks[SIG_MIN_SIGNAL];
!(sigs & *bitMaskPtr);
sigNum++, bitMaskPtr++) {
}
SigClearPendingMask(procPtr, sigNum);
/*
* Process the signal.
*/
switch (procPtr->sigActions[sigNum]) {
case SIG_IGNORE_ACTION:
printf("Warning: %s\n",
"Sig_Handle: An ignored signal was in a signal pending mask.");
return(FALSE);
case SIG_KILL_ACTION:
if (sigNum == SIG_KILL || !(procPtr->genFlags & PROC_DEBUGGED)) {
Proc_ExitInt(PROC_TERM_SIGNALED, sigNum,
procPtr->sigCodes[sigNum]);
panic("Sig_Handle: Proc_Exit returned!\n");
} else {
/* Fall through */
}
case SIG_SUSPEND_ACTION:
case SIG_DEBUG_ACTION:
/*
* A suspended process and a debugged process are basically
* the same. A suspended process can be debugged just like
* a process in the debug state. The only difference is that
* a suspended process does not go onto the debug list; it can
* only be debugged by a debugger that specifically asks for
* it.
*
* Suspend the process.
*/
Proc_SuspendProcess(procPtr,
procPtr->sigActions[sigNum] == SIG_DEBUG_ACTION,
PROC_TERM_SIGNALED, sigNum,
procPtr->sigCodes[sigNum]);
return(FALSE);
case SIG_MIGRATE_ACTION:
/*
* If the process was in the middle of a page fault,
* its PC in the trap stack is not useable.
* Reset the pending condition but hold it until we get out of
* the kernel.
*/
if (!Mach_CanMigrate(procPtr)) {
LocalSend(procPtr, sigNum, procPtr->sigCodes[sigNum],
(Address)procPtr->sigAddr);
procPtr->sigHoldMask |= Sig_NumberToMask(SIG_MIGRATE_TRAP);
return(FALSE);
}
/*
* Double-check against process not allowed to migrate. This
* can happen if a process migrates, opens a pdev as master,
* and gets signalled to migrate home.
*/
if (procPtr->genFlags & PROC_DONT_MIGRATE) {
if (proc_MigDebugLevel > 0) {
printf("Proc_Migrate: process %x is not allowed to migrate.\n",
procPtr->processID);
}
return(FALSE);
}
if (procPtr->peerHostID != NIL) {
if (proc_MigDebugLevel > 6) {
printf("Sig_Handle calling Proc_MigrateTrap for process %x.\n",
procPtr->processID);
}
Proc_MigrateTrap(procPtr);
}
return(FALSE);
case SIG_DEFAULT_ACTION:
panic("Sig_Handle: SIG_DEFAULT_ACTION found in array of actions?\n");
}
/*
* Set up our part of the signal stack.
*/
sigStackPtr->sigNum = sigNum;
sigStackPtr->sigCode = procPtr->sigCodes[sigNum];
sigStackPtr->sigAddr = procPtr->sigAddr;
/*
* If this signal handler is being called after a call to Sig_Pause then
* the real signal hold mask has to be restored after the handler returns.
* This is assured by pushing the real hold mask which is stored in
* the proc table onto the stack.
*/
if (procPtr->sigFlags & SIG_PAUSE_IN_PROGRESS) {
procPtr->sigFlags &= ~SIG_PAUSE_IN_PROGRESS;
sigStackPtr->contextPtr->oldHoldMask = procPtr->oldSigHoldMask;
} else {
sigStackPtr->contextPtr->oldHoldMask = procPtr->sigHoldMask;
}
procPtr->sigHoldMask |= procPtr->sigMasks[sigNum];
sigBitMask = sigBitMasks[sigNum];
if (sigBitMask & ~sigCanHoldMask) {
/*
* If this is a non-blockable signal then add it to the hold mask
* so that if we get it again we know that it can't be handled.
*/
procPtr->sigHoldMask |= sigBitMask;
}
procPtr->specialHandling = 1;
*pcPtr = (Address)procPtr->sigActions[sigNum];
return(TRUE);
}
/*
*----------------------------------------------------------------------
*
* Sig_Return --
*
* Process a return from signal.
*
* Results:
* None.
*
* Side effects:
* The trap stack is modified.
*
*----------------------------------------------------------------------
*/
void
Sig_Return(procPtr, sigStackPtr)
register Proc_ControlBlock *procPtr; /* Process that is returning
* from a signal. */
Sig_Stack *sigStackPtr; /* Signal stack. */
{
procPtr->sigHoldMask = sigStackPtr->contextPtr->oldHoldMask;
procPtr->specialHandling = 1;
}
/*
*----------------------------------------------------------------------
*
* Sig_AllowMigration --
*
* Set up a process to allow migration. This is a special call
* because normally the SIG_MIGRATE_TRAP signal is not holdable in
* the first place.
*
* This could be a macro and be called directly from
* Mach_StartUserProc, once things are stable....
*
* Results:
* None.
*
* Side effects:
* The process's hold mask is modified.
*
*----------------------------------------------------------------------
*/
void
Sig_AllowMigration(procPtr)
register Proc_ControlBlock *procPtr; /* process to modify */
{
if (procPtr->sigHoldMask &&
(procPtr->sigHoldMask & sigBitMasks[SIG_MIGRATE_TRAP])) {
procPtr->sigHoldMask &= ~sigBitMasks[SIG_MIGRATE_TRAP];
procPtr->specialHandling = 1;
}
}
/*
*----------------------------------------------------------------------
*
* Sig_CheckForKill --
*
* Check if a process has a kill signal and kill it if so.
* Otherwise return. this is for calling in difficult places where
* we can't allow any signals that would be handled in user mode to
* occur.
*
* Results:
* None.
*
* Side effects:
* Process may be killed.
*
*----------------------------------------------------------------------
*/
void
Sig_CheckForKill(procPtr)
Proc_ControlBlock *procPtr;
{
int sigs;
/*
* Find out which signals are pending.
*/
sigs = procPtr->sigPendingMask & ~procPtr->sigHoldMask;
if (sigs == 0) {
return;
}
/*
* Check for the signal SIG_KILL. This is processed specially because
* it is how processes that have some problem such as being unable
* to write to swap space on the file server are destroyed.
*/
if (sigs & sigBitMasks[SIG_KILL]) {
if (procPtr->sigCodes[SIG_KILL] != SIG_NO_CODE) {
Proc_ExitInt(PROC_TERM_DESTROYED,
procPtr->sigCodes[SIG_KILL], 0);
} else {
Proc_ExitInt(PROC_TERM_SIGNALED, SIG_KILL, 0);
}
}
return;
}